
package Control;

import Entidad.AC;
import java.util.logging.Level;
import java.util.logging.Logger;

public class Sandpile implements Runnable {

    private int minI;
    private int minJ;
    private int maxI;
    private int maxJ;
    private AC[][] acMatrix;
    private int[][] nextStep;
    private boolean stop;
    private int speed;

    public Sandpile(int minx, int miny, int maxx, int maxy, int numCells) {
        
        this.minI = minx;
        this.minJ = miny;
        this.maxI = maxx;
        this.maxJ = maxy;
        this.acMatrix = new AC[numCells][numCells];
        this.nextStep = new int[numCells][numCells];
        createACMatrix();
        setNeighbors();
    }

    private int getI(int i) {
        if (i < minI) {
            return maxI;
        }
        if (i > maxI) {
            return minI;
        }
        return i;
    }

    private int getJ(int j) {
        if (j < minI) {
            return maxI;
        }
        if (j > maxI) {
            return minI;
        }
        return j;
    }

    private void createACMatrix() {
        for (int i = this.minI; i <= this.maxI; i++) {
            for (int j = this.minJ; j <= this.maxJ; j++) {
                acMatrix[i][j] = new AC(i, j);
            }
        }
    }

    /**
     *
     * @param automata una instancia de AC particular
     * @return un array con los vecinos del automata
     */
    private AC[] getNeighbors(AC automata) {
        AC[] neighbors = new AC[4];
        int i = automata.getI();
        int j = automata.getJ();

        int currenti;
        int currentj;

        //Primer vecino: (i-1)(j)
        currenti = getI(i - 1);
        currentj = getJ(j);

        neighbors[0] = acMatrix[currenti][currentj];

        //Segundo vecino: (i),(j-1)
        currenti = getI(i);
        currentj = getJ(j - 1);

        neighbors[1] = acMatrix[currenti][currentj];        

        //Tercer vecino: (i),(j+1)
        currenti = getI(i);
        currentj = getJ(j + 1);

        neighbors[2] = acMatrix[currenti][currentj];       

        //Cuarto vecino : (i+1),(j)
        currenti = getI(i + 1);
        currentj = getJ(j);

        neighbors[3] = acMatrix[currenti][currentj];
        
        return neighbors;
    }

    private void setNeighbors() {
        AC[] neighbors;
        for (int i = this.minI; i <= this.maxI; i++) {
            for (int j = this.minJ; j <= this.maxJ; j++) {
                neighbors = getNeighbors(acMatrix[i][j]);
                acMatrix[i][j].setNeighbors(neighbors);
            }
        }
    }

    public AC getAC(int i, int j) {
        return this.acMatrix[i][j];
    }

    public void begin() throws InterruptedException {
        System.out.println("Begin");
        this.stop = false;
        //for (int i = 0; i < 10 ; i++){
        play();

        //}

    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public void stop() {
        this.stop = true;
    }

    private void play() throws InterruptedException {
        calculateNextStep();
        changeGlobalState();
        
    }

    private void calculateNextStep() {
        for (int i = this.minI; i <= this.maxJ; i++) {
            for (int j = this.minI; j <= this.maxJ; j++) {
                nextStep[i][j] = acMatrix[i][j].calculateNextStep();
            }
        }

    }

    private void changeGlobalState() throws InterruptedException {
        for (int i = this.minI; i <= this.maxI; i++) {
            for (int j = this.minJ; j <= this.maxJ; j++) {
                if (nextStep[i][j] == 0) {
                    acMatrix[i][j].setEstado(0);
                } else if(nextStep[i][j] == 1) {
                    acMatrix[i][j].setEstado(1);
                } else if(nextStep[i][j] == 2) {
                    acMatrix[i][j].setEstado(2);
                } else if(nextStep[i][j] == 3) {
                    acMatrix[i][j].setEstado(3);
                } else if(nextStep[i][j] == 4) {
                    acMatrix[i][j].setEstado(4);
                }
            }
        }
        Thread.sleep(speed);
    }

    @Override
    public void run() {
        this.stop = false;
        while (!stop) {
            try {
                play();
            } catch (InterruptedException ex) {
                Logger.getLogger(Sandpile.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }

    public void clear() {
        for (int i = this.minI; i <= this.maxI; i++) {
            for (int j = this.minJ; j <= this.maxJ; j++) {
                acMatrix[i][j].calculateNextStep();
            }
        }
    }
}
